package com.ruibang.glass.file.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruibang.glass.common.domain.file.FileBusiness;
import com.ruibang.glass.file.config.FileProperties;
import com.ruibang.glass.file.constant.Constants;
import com.ruibang.glass.file.constant.FileErrorCode;
import com.ruibang.glass.file.domain.req.FileInfoReq;
import com.ruibang.glass.file.domain.req.PreviewFileReq;
import com.ruibang.glass.file.entity.FileInfo;
import com.ruibang.glass.file.mapper.FileMapper;
import com.ruibang.glass.file.service.FileService;
import com.teaming.cloud.framework2.common.constant.CommonConstant;
import com.teaming.cloud.framework2.common.exception.ServiceException;
import com.teaming.cloud.framework2.common.pojo.PageResult;
import com.teaming.cloud.framework2.minio.component.MinioTemplate;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.util.*;
import java.util.stream.Collectors;

@Service
public class FileServiceImpl extends ServiceImpl<FileMapper, FileInfo> implements FileService {

    public static final String SERVER_TYPE_OTHER = "other";

    @Autowired
    private MinioTemplate minioTemplate;

    @Autowired
    private FileProperties fileProperties;

    @Override
    public String uploadFile(MultipartFile file) {
        String originalFilename = file.getOriginalFilename();
        int index = originalFilename.lastIndexOf(Constants.SpecialSign.POINT.getName());
        String fileType = originalFilename.substring(index + 1);
        String bucketName = getBucketByType(fileType);
        if (StringUtils.isNotBlank(bucketName)) {
            boolean exist = minioTemplate.bucketExists(bucketName, getServerType(fileType));
            if (!exist) {
                minioTemplate.makeBucket(bucketName, getServerType(fileType));
            }
            FileInfo fileInfo = new FileInfo();
            String name = originalFilename.substring(0, index);
            fileInfo.setFileName(name);
            fileInfo.setStatus(Constants.ZERO_STR);
            fileInfo.setFileType(fileType);
            fileInfo.setBelongBucket(bucketName);
            fileInfo.setFileSize(file.getSize());
            String uuid = UUID.randomUUID().toString().replace("-", "");
            fileInfo.setDisturbCode(uuid);
            String fileName = minioTemplate.upload(file, bucketName, uuid.concat(Constants.SpecialSign.RIGHTSLASH.getName().concat(originalFilename)), getServerType(fileType));
            if (StringUtils.isNotBlank(fileName)) {
                this.save(fileInfo);
                return fileInfo.getFileId();
            }
        }
        throw new ServiceException(FileErrorCode.UPLOAD_FILE_IS_FAILED.getCode(), FileErrorCode.UPLOAD_FILE_IS_FAILED.getMsg());
    }

    @Override
    public List<String> uploadFiles(List<MultipartFile> files) {
        if (CollectionUtils.isNotEmpty(files)) {
            List<String> fileIds = new ArrayList<>();
            for (MultipartFile file : files) {
                String originalFilename = file.getOriginalFilename();
                int index = originalFilename.lastIndexOf(Constants.SpecialSign.POINT.getName());
                String fileType = originalFilename.substring(index + 1);
                String bucketName = getBucketByType(fileType);
                if (StringUtils.isNotBlank(bucketName)) {
                    boolean exist = minioTemplate.bucketExists(bucketName, getServerType(fileType));
                    if (!exist) {
                        minioTemplate.makeBucket(bucketName, getServerType(fileType));
                    }
                    FileInfo fileInfo = new FileInfo();
                    String name = originalFilename.substring(0, index);
                    fileInfo.setFileName(name);
                    fileInfo.setStatus(Constants.ZERO_STR);
                    fileInfo.setFileType(fileType);
                    fileInfo.setBelongBucket(bucketName);
                    fileInfo.setFileSize(file.getSize());
                    String uuid = UUID.randomUUID().toString().replace("-", "");
                    fileInfo.setDisturbCode(uuid);
                    String fileName = minioTemplate.upload(file, bucketName, uuid.concat(Constants.SpecialSign.RIGHTSLASH.getName().concat(originalFilename)), getServerType(fileType));
                    if (StringUtils.isNotBlank(fileName)) {
                        this.save(fileInfo);
                        fileIds.add(fileInfo.getFileId());
                    }
                }
            }

            if (CollectionUtils.isNotEmpty(fileIds)) {
                return fileIds;
            }
        }

        throw new ServiceException(FileErrorCode.UPLOAD_FILE_IS_FAILED.getCode(), FileErrorCode.UPLOAD_FILE_IS_FAILED.getMsg());
    }

    @Override
    public String previewFile(PreviewFileReq fileReq) {
        FileInfo file = getById(fileReq.getFileId());
        if (file == null || StringUtils.isBlank(file.getFileId())) {
            throw new ServiceException(FileErrorCode.FILE_IS_NOT_EXIST.getCode(), FileErrorCode.FILE_IS_NOT_EXIST.getMsg());
        }
        return getPathByFile(file, fileReq.getIsRemote());
    }

    @Override
    public List<String> getPreviewByBusinessParam(FileBusiness fileBusiness) {
        List<String> addresses = new ArrayList<>();
        List<FileInfo> files = baseMapper.getPreviewByBusinessParam(fileBusiness);
        if (CollectionUtils.isNotEmpty(files)) {
            for (FileInfo file : files) {
                addresses.add(getPathByFile(file, CommonConstant.ZERO_STR));
            }
        }
        return addresses;
    }

    @Override
    public List<FileInfo> previewFiles(PreviewFileReq previewFileReq) {
        if (CollectionUtils.isNotEmpty(previewFileReq.getFileIds())) {
            List<FileInfo> files = this.listByIds(previewFileReq.getFileIds());
            if (CollectionUtils.isNotEmpty(files)) {
                for (FileInfo file : files) {
                    file.setFileAddress(getPathByFile(file, previewFileReq.getIsRemote()));
                }
            }
            return files;
        }

        return new ArrayList<>();
    }

    @Override
    public List<FileInfo> previewLocalFiles(List<String> fileIds) {
        if (CollectionUtils.isNotEmpty(fileIds)) {
            List<FileInfo> files = this.listByIds(fileIds);
            if (CollectionUtils.isNotEmpty(files)) {
                for (FileInfo file : files) {
                    file.setFileAddress(getPathByFile(file, CommonConstant.ZERO_STR));
                }
            }
            return files;
        }

        return new ArrayList<>();
    }

    private String getPathByFile(FileInfo file, String isRemote) {
        String fileName = file.getDisturbCode().concat(Constants.SpecialSign.RIGHTSLASH.getName()
                .concat(file.getFileName()).concat(Constants.SpecialSign.POINT.getName().concat(file.getFileType())));
        String bucketName = getBucketByType(file.getFileType());
        String serverType = getServerType(file.getFileType());
        String path = minioTemplate.preview(fileName, bucketName, serverType);
        if (Constants.ONE_STR.equals(isRemote)) {
            String indexValue = Constants.SpecialSign.RIGHTSLASH.getName()
                    .concat(bucketName.concat(Constants.SpecialSign.RIGHTSLASH.getName()).concat(file.getDisturbCode()));
            String value = path.substring(0, path.indexOf(indexValue));
            if (SERVER_TYPE_OTHER.equals(serverType)) {
                path = path.replace(value, fileProperties.getOtherRemote());
            } else {
                path = path.replace(value, fileProperties.getRemoteAddress());
            }
        }
        return path;
    }

    @Override
    public void deleteById(String fileId) {
        baseMapper.updateStatusById(fileId, Constants.ONE_STR);
    }

    @Override
    public PageResult<FileInfo> queryPage(FileInfoReq infoReq) {
        LambdaQueryWrapper<FileInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(infoReq.getFileName())) {
            lambdaQueryWrapper.like(FileInfo::getFileName, "%" + infoReq.getFileName() + "%");
        }
        if (StringUtils.isNotBlank(infoReq.getFileType())) {
            lambdaQueryWrapper.like(FileInfo::getFileType, "%" + infoReq.getFileType() + "%");
        }

        lambdaQueryWrapper.orderByDesc(FileInfo::getCreateTime);

        return baseMapper.selectPage(infoReq, lambdaQueryWrapper);
    }


    @Override
    public List<FileInfo> queryFiles(FileInfo info) {
        LambdaQueryWrapper<FileInfo> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        if (StringUtils.isNotBlank(info.getFileName())) {
            lambdaQueryWrapper.like(FileInfo::getFileName, "%" + info.getFileName() + "%");
        }
        if (StringUtils.isNotBlank(info.getFileType())) {
            lambdaQueryWrapper.like(FileInfo::getFileType, "%" + info.getFileType() + "%");
        }
        lambdaQueryWrapper.orderByDesc(FileInfo::getCreateTime);

        return baseMapper.selectList(lambdaQueryWrapper);
    }

    @Override
    public void updateBusinessById(FileBusiness fileBusiness) {
        baseMapper.updateBusinessById(fileBusiness);
    }


    @Override
    public void deleteByBusinessId(String businessId) {
        baseMapper.updateByBusinessId(businessId);
    }

    @Override
    public void deleteByFileIds(List<String> fileIds) {
        baseMapper.deleteBatchIds(fileIds);
        //删除桶中对应文件
        List<FileInfo> fileInfos = baseMapper.queryFileByIds(fileIds);
        if (CollectionUtils.isNotEmpty(fileInfos)) {
            for (FileInfo fileInfo : fileInfos) {
                String fileName = fileInfo.getDisturbCode().concat(
                        File.separator.concat(fileInfo.getFileName()
                                .concat(CommonConstant.SpecialSign.POINT.getName().concat(fileInfo.getFileName()))));
                System.out.println("======" + fileName);
                minioTemplate.remove(fileName, fileInfo.getBelongBucket(), getServerType(fileInfo.getFileType()));
            }
        }

    }

    @Override
    public void deleteByBusinessIdAndAttribute(FileBusiness fileBusiness) {
        baseMapper.updateByFileBusiness(fileBusiness);
    }

    @Override
    public List<String> getFileIdsByBusinessId(String businessId) {
        return baseMapper.getFileIdsByBusinessId(businessId);
    }


    @Override
    public List<FileBusiness> getFileBusinessByIds(List<String> businessIds) {
        List<FileInfo> fileInfos = baseMapper.getFileBusinessByIds(businessIds);
        List<FileBusiness> fileBusinesses = new ArrayList<>();
        if (CollectionUtils.isNotEmpty(fileInfos)) {
            Map<String, List<FileInfo>> fileMap = fileInfos.stream().collect(Collectors.groupingBy(f ->
                    f.getBusinessId().concat(getAttribute(f.getAttribute())==null?"":getAttribute(f.getAttribute()))));
            fileMap.forEach((k, v) -> {
                FileBusiness fileBusiness = new FileBusiness();
                fileBusiness.setFileIds(v.stream().map(f -> f.getFileId()).collect(Collectors.toList()));
                fileBusiness.setBusinessId(k);
                fileBusiness.setAttribute(v.get(0).getAttribute());
                fileBusinesses.add(fileBusiness);
            });
        }
        return fileBusinesses;
    }

    @Override
    public FileInfo getDetails(String businessId) {
        FileInfo fileInfo = lambdaQuery().eq(FileInfo::getBusinessId, businessId).one();
        if (fileInfo != null) {
            fileInfo.setFileAddress(getPathByFile(fileInfo, Constants.ZERO_STR));
        }
        return fileInfo;
    }

    @Override
    public void downLoad(String id, HttpServletResponse response) {
        FileInfo fileInfo = baseMapper.selectById(id);
        String fileName = fileInfo.getDisturbCode().concat(Constants.SpecialSign.RIGHTSLASH.getName()
                .concat(fileInfo.getFileName()).concat(Constants.SpecialSign.POINT.getName().concat(fileInfo.getFileType())));
        String bucketName = getBucketByType(fileInfo.getFileType());
        String serverType = getServerType(fileInfo.getFileType());
        minioTemplate.download(fileName, serverType, bucketName, response);
    }

    @Override
    public List<FileInfo> getFileInfoByFileIds(List<String> fileIds) {
        if (CollectionUtils.isNotEmpty(fileIds)) {
            List<FileInfo> fileInfos = lambdaQuery().in(FileInfo::getFileId, fileIds).list();
            if (CollectionUtils.isNotEmpty(fileInfos)) {
                for (FileInfo fileInfo : fileInfos) {
                    fileInfo.setFileAddress(getPathByFile(fileInfo, Constants.ZERO_STR));
                }
            }
            return fileInfos;
        }
        return Collections.emptyList();
    }


    @Override
    public List<FileInfo> getFileInfoByBusinessIds(List<String> businessIds) {
        if (CollectionUtils.isNotEmpty(businessIds)) {
            List<FileInfo> fileInfos = lambdaQuery().in(FileInfo::getBusinessId, businessIds).list();
            if (CollectionUtils.isNotEmpty(fileInfos)) {
                for (FileInfo fileInfo : fileInfos) {
                    fileInfo.setFileAddress(getPathByFile(fileInfo, Constants.ZERO_STR));
                }
            }
            return fileInfos;
        }
        return Collections.emptyList();
    }

    /***
     * 判断属性是否为空
     * @param attribute
     * @return
     */
    private String getAttribute(String attribute) {
        if (StringUtils.isNotBlank(attribute)) {
            return attribute;
        } else {
            return "";
        }
    }

    /**
     * 根据type返回bucketName
     *
     * @param type
     * @return
     */
    private String getBucketByType(String type) {
        if (fileProperties.getImageType().contains(type)) {
            return Constants.FileType.IMAGE.getName();
        } else if (fileProperties.getVideoType().contains(type)) {
            return Constants.FileType.VIDEO.getName();
        } else if (fileProperties.getOfficeType().contains(type)) {
            return Constants.FileType.OFFICE.getName();
        } else {
            return Constants.FileType.OTHER.getName();
        }
    }

    private String getServerType(String type) {
        if (fileProperties.getOfficeType().contains(type)) {
            return SERVER_TYPE_OTHER;
        } else if (fileProperties.getOtherType().contains(type)) {
            return SERVER_TYPE_OTHER;
        } else {
            return "";
        }
    }
}
